﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PetGo
{
    class Game
    {
        //public static string k_gtp_koordinaten = "abcdefghjklmnopqrstuvwxyz";

        public static string myInput="";
        public static string myChatString = "=";
        public static int mymaxPlayouts = 100000;
        public Board myBoard;
        public double myKomi;
        public int myhandicap = 0;
        public int mygreedyKomi = 0;
        public double myDynamicKomi = Double.MinValue;
        public double myResignProbability;
        public int mycounter;
        public int mysecondstothink;
        public Tree theRoot;
        public string myName;

        static List<int> handicapList = new List<int>();
        
        public bool myisGTP;
        public bool myisGTPDebug = false;
        public int myRegression = 0;
        bool doquit = false;
        bool doplay = false;
        bool dogen = false;
        bool doclear = false;
        bool dopass = false;


        public Game(double theKomi, int thesecondstothink)
        {
            handicapList.Add(60);
            handicapList.Add(300);
            handicapList.Add(72);
            handicapList.Add(288);
            //handicapList.Add(180);
            handicapList.Add(174);
            handicapList.Add(186);
            handicapList.Add(66);
            handicapList.Add(294);
            myKomi = theKomi;
            myResignProbability = 15;
            mysecondstothink = thesecondstothink;
            myName = getName();
        }
        private string getName()
        {
            try
            {
                string thepath = AppDomain.CurrentDomain.BaseDirectory.Replace("\\", "");
                int test = thepath.ToLower().IndexOf("petgo");
                thepath = thepath.Substring(test + 1);
                test = thepath.ToLower().IndexOf("petgo");
                return thepath.Substring(test, Math.Min(6, thepath.Length - test)) + "_" + DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString("00") + DateTime.Now.Day.ToString("00") + DateTime.Now.Hour.ToString("00") + DateTime.Now.Minute.ToString("00");
            }
            catch
            {
                return DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString("00") + DateTime.Now.Day.ToString("00") + DateTime.Now.Hour.ToString("00") + DateTime.Now.Minute.ToString("00") + DateTime.Now.Second.ToString("00");
            }
        }
        /// <summary>
        /// gets best move
        /// </summary>
        /// <returns>position of the top move</returns>
        public int getTopMove()
        {
            theRoot = new Tree(null, myBoard);

            if (theRoot == null || myRegression>0)
            {
                theRoot = new Tree(null, myBoard);
            }
            theRoot.doTreeRoot();
            int thePosition = -2;
            double maxWert = Double.MinValue;
            double maxQr = Double.MinValue;
            foreach (Tree theChildnode in theRoot.mychilds.Values)
            {
                if (theChildnode.vorkommen > maxWert || (theChildnode.vorkommen == maxWert && theChildnode.qr > maxQr))
                {
                    maxWert = theChildnode.vorkommen;
                    maxQr = theChildnode.qr;
                    thePosition = theChildnode.pos;
                }
            }
            mycounter = theRoot.vorkommen;
            if (thePosition >= 0)
            {
                if (
                    (theRoot.mychilds[thePosition].q * 100 < myResignProbability && theRoot.mychilds[thePosition].qr * 100 < myResignProbability && myBoard.mytoMove == color.black && mygreedyKomi <= 0)
                    || (theRoot.mychilds[thePosition].q * 100 < myResignProbability && theRoot.mychilds[thePosition].qr * 100 < myResignProbability && myBoard.mytoMove == color.white && mygreedyKomi >= 0)
                   )
                {
                    myBoard.myWinner = myBoard.getopponent(myBoard.mytoMove);
                    return -1;
                }
                return thePosition;
            }
            return thePosition;
        }
        public void regression()
        {
            int regr = 0;
            int whitewins = 0;
            int blackwins = 0;
            while (regr < myRegression)
            {
                ++regr;
                play();
                if(myBoard.myWinner == color.black)
                {
                    blackwins++;
                }
                else
                {
                    whitewins++;
                }
                Console.WriteLine("White: " + whitewins.ToString() + " Black: " + blackwins.ToString());
                int temp = myBoard.myboardsize;
                
                Board theBoard = new Board(temp, this);
                myBoard = theBoard;
                myBoard.initBoard();
            }

        }
        /// <summary>
        /// plays out a whole game until resign of one side
        /// </summary>
        public void play()
        {
            bool goon = true;
            bool ispassed = false;
            while (goon)
            {
                int move = getTopMove();
                switch(move)
                {
                    case -2:
                        if (ispassed == true) goon = false;
                        ispassed = true;
                        refreshRootTree(0, true);
                        break;
                    case -1:
                        goon = false;
                        break;
                    default:
                        ispassed = false;
                        myBoard.place(move, myBoard.mytoMove);
                        myBoard.changemycolortoMove();
                        refreshRootTree(move, false);
                        if (myRegression == 0)
                        {
                            myBoard.fillEmptyFieldsList();
                            myBoard.writeboard();
                            Console.WriteLine("Count: " + mycounter.ToString());
                        }
                        break;
                }
               
            }

        }

        private void refreshRootTree(int move, bool createnew)
        {
            theRoot = new Tree(null, myBoard);
            if (!createnew && theRoot != null && theRoot.mychilds != null && theRoot.mychilds.ContainsKey(move))
            {
                theRoot.mychilds[move].initAsRootTree(myBoard);
                theRoot = theRoot.mychilds[move];
            }
            else
            {
                theRoot = new Tree(null, myBoard);
            }
            //Tree.myRootBoard.checkBoard(color.black);
            //Tree.myRootBoard.checkBoard(color.white);
        }

        public void doit()
        {
            clear();
            string arg = "";
            while (true)
            {
                doquit = false;
                doplay = false;
                dogen = false;
                doclear = false;
                while (true)
                {
                    arg = myInput;
                    if (arg != null && arg != "")
                    {

                        if (arg == "name")
                        {
                            Console.WriteLine("=petgo");
                            Console.WriteLine();
                            break;
                        }
                        if (arg.StartsWith("kgs-chat"))
                        {
                            Console.WriteLine(myChatString);
                            Console.WriteLine();
                            break;
                        }
                        if (arg == "version")
                        {
                            Console.WriteLine("=0.701, Merry Christmas and a happy new year :-) ");
                            Console.WriteLine();
                            break;
                        }
                        if (arg == "protocol_version")
                        {
                            Console.WriteLine("=2");
                            Console.WriteLine();
                            break;
                        }
                        if (arg == "final_status_list dead")
                        {
                            Console.WriteLine("=");
                            foreach (Group cGroup in Tree.myRootBoard.myGroup.Values)
                            {
                                if (cGroup.dead > cGroup.alive)
                                {
                                    foreach (int pos in cGroup.myFields.Keys)
                                    {
                                        Console.WriteLine(Sgf.get_Sgf_Position(pos, myBoard.myboardsize));
                                    }
                                }

                            }
                            Console.WriteLine();
                            break;
                        } 
                        if (arg == "final_status_list alive")
                        {
                            foreach (Group cGroup in Tree.myRootBoard.myGroup.Values)
                            {
                                if (cGroup.dead <= cGroup.alive)
                                {
                                    foreach (int pos in cGroup.myFields.Keys)
                                    {
                                        Console.WriteLine(Sgf.get_Sgf_Position(pos, myBoard.myboardsize));
                                    }
                                }

                            }
                            Console.WriteLine();
                            break;
                        }
                        if (arg.ToLower().StartsWith("set_free_handicap"))
                        {
                            this.myhandicap = 0;
                            string[] thesplit = arg.Substring(17).Split(' ');
                            foreach (string thetemp in thesplit)
                            {
                                if (thetemp.Trim().Length > 0)
                                {
                                    this.myBoard.place(Sgf.get_Move_Position(thetemp, myBoard.myboardsize), color.black);

                                    this.myhandicap += 1;
                                }
                            }
                            this.myBoard.mymovehistory.Clear();
                            theRoot = new Tree(null, myBoard);
                            Console.WriteLine("=");
                            Console.WriteLine("#" + arg.Substring(17));

                        }
                        if (arg.ToLower().StartsWith("place_free_handicap"))
                        {
                            this.myhandicap = int.Parse(arg.Substring(19));
                            int temphandi = myhandicap;
                            if (myhandicap > 4 && myhandicap%2 == 1) temphandi -= 1;
                            string thelist = "";
                            for (int i = 0; i < temphandi; ++i)
                            {
                                thelist += Sgf.get_Sgf_Position(handicapList[i], myBoard.myboardsize)+" ";
                                this.myBoard.place(handicapList[i], color.black);
                            }
                            if (myhandicap % 2 == 1 && temphandi != myhandicap)
                            {
                                thelist += Sgf.get_Sgf_Position(180, myBoard.myboardsize) + " ";
                                this.myBoard.place(180, color.black);
                            }
                            
                            this.myBoard.mymovehistory.Clear();
                            theRoot = new Tree(null, myBoard);
                            Console.WriteLine("="+thelist);
                            Console.WriteLine();
                            break;
                        }
                       
                        if (arg == "quit")
                        {
                            Console.WriteLine("=");
                            Console.WriteLine();
                            doquit = true;
                            break;
                        }
                        if (arg == "list_commands")
                        {
                            //if (myTournamentMode)
                            //{
                            //    Console.WriteLine("=final_status_list");
                            //}
                            Console.WriteLine("=quit");
                            Console.WriteLine("name");
                            Console.WriteLine("komi");
                            Console.WriteLine("kgs-chat");
                            Console.WriteLine("time_settings");
                            Console.WriteLine("clear_board");
                            Console.WriteLine("boardsize");
                            Console.WriteLine("final_status_list");
                            Console.WriteLine("set_free_handicap");
                            Console.WriteLine("place_free_handicap");

                            Console.WriteLine();
                            break;
                        }
                        if (arg.StartsWith("boardsize"))
                        {
                            myBoard.myboardsize = int.Parse(arg.Substring(10));
                            Console.WriteLine("=");
                            Console.WriteLine();
                            break;
                        }
                        if (arg.StartsWith("komi"))
                        {
                            try
                            {
                                myKomi = double.Parse(arg.Trim().Substring(4), System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US")); 
                                Console.WriteLine("=");
                                Console.WriteLine();
                            }
                            catch (Exception ex)
                            {
                                myKomi = 7.5;
                                Console.WriteLine("=");
                                Console.WriteLine();
                            }
                            break;
                        }
                        if (arg.StartsWith("clear_board"))
                        {
                            //nada
                            doclear = true;
                            break;
                        }
                        if (arg.StartsWith("time_settings"))
                        {
                            string[] thearg = arg.Split(' ');
                            //nada
                            int s = int.Parse(thearg[3]);
                            if (s > 0)
                            {
                                mysecondstothink = int.Parse(thearg[2]) / s;
                            }
                            Console.WriteLine("=");
                            Console.WriteLine();
                            break;
                        }
                        if (arg.StartsWith("genmove"))
                        {
                            dogen = true;
                            break;
                        }
                        if (arg.StartsWith("play"))
                        {
                            doplay = true;
                            dopass = false;
                            break;
                        }
                        if (arg != "")
                        {
                            Console.WriteLine("=");
                            Console.WriteLine();
                            arg = "";
                            break;
                        }
                    }
                    // here think ahead
                    if (theRoot != null)
                    {
                        //if (theRoot.vorkommen < mymaxPlayouts && myBoard.mymovehistory.Count > 0)
                        //{
                        //    if (theRoot.vorkommen > mymaxPlayouts / 10)
                        //    {
                        //        Thread.Sleep(10);
                        //    }
                        //    Thread.Sleep(1);
                        //    int anz = theRoot.vorkommen;
                        //    anz = theRoot.enlargeTree(anz);
                        //}
                        //else
                        {
                            Thread.Sleep(10);
                        }
                    }
                }
                if (doclear)
                {
                    clear();
                    Console.WriteLine("=");
                    Console.WriteLine();
                    myName = getName();
                    myBoard.initBoard();
                }
                if (doquit)
                {
                    clear();
                    //doit();
                    //Console.WriteLine("=");
                    //Console.WriteLine();

                }
                if (doplay)
                {
                    string ccolor = "";
                    string letter = "";
                    string number = "";
                    int position = 0;
                    try
                    {
                        string temp = arg.ToLower().Replace("play", "").Trim();
                        ccolor = temp.Substring(0, 1);
                        color thecolor = color.white;
                        if (ccolor == "b") thecolor = color.black;
                        temp = temp.Substring(1).ToLower().Trim();
                        // pass
                        if (temp == "pass")
                        {
                            myBoard.changemycolortoMove();
                            dopass = true;
                            //refreshRootTree(0, true);
                        }
                        else
                        {
                            //letter = temp.Substring(0, 1);
                            //temp = temp.Substring(1).Trim();
                            //number = temp;
                            //int y = int.Parse(number);
                            //int x = Sgf.k_gtp_koordinaten.IndexOf(letter);
                            //position = (myBoard.myboardsize) * (y-1) + x;
                            myBoard.place(Sgf.get_Move_Position(temp, myBoard.myboardsize), thecolor);
                            myBoard.changemycolortoMove();
                            refreshRootTree(Sgf.get_Move_Position(temp, myBoard.myboardsize), false);

                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("? "
                            + "arg: " + arg
                            + " color: " + ccolor
                            + " letter: " + letter
                            + " number: " + number
                            + " position: " + position.ToString()
                            + " size of board: " + myBoard.myboardsize.ToString()
                            + " Error: " + ex.Message);
                        Console.WriteLine();

                    }
                    Console.WriteLine("=" + ccolor + " " + letter + number);
                    Console.WriteLine();
                }
                if (dogen)
                {

                    string ccolor = "";
                    color thecolor = color.white;
                    try
                    {
                        string temp = arg.ToLower().Replace("genmove", "").Trim();
                        ccolor = temp.Substring(0, 1);
                        if (ccolor == "b") thecolor = color.black;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("? "
                            + "arg: " + arg
                            + " color: " + ccolor
                            + " Error: " + ex.Message);
                        Console.WriteLine();

                    }
                    myBoard.mytoMove = thecolor;

                    //int tempthinking = mysecondstothink;

                    //if (dopass)
                    //{
                    //    mysecondstothink = 1;
                    //}
                    int themove = -2;
                    // passing -> play neutrals
                    if (dopass)
                    {
                        // neutral moves
                        themove = myBoard.getanyNeutralMove(themove);
                        refreshRootTree(-1, true);
                    }
                    if (themove == -2)
                    {
                        themove = getTopMove();
                        //mysecondstothink = tempthinking;
                        if ((theRoot.getTopChild() == null && myBoard.mymovehistory.Count > 0)
                            || (dopass && theRoot.getTopChild().q * 100 > 100 - myResignProbability && theRoot.getTopChild().qr * 100 > 100 - myResignProbability )
                            || (dopass && theRoot.getTopChild().q * 100 > 50 && theRoot.getTopChild().qr * 100 > 50 && mygreedyKomi > 0 && myBoard.mytoMove == color.black)
                            || (dopass && theRoot.getTopChild().q * 100 > 50 && theRoot.getTopChild().qr * 100 > 50 && mygreedyKomi < 0 && myBoard.mytoMove == color.white))
                        {
                            //pass if game is clear
                            themove = -2;
                            //Console.WriteLine("=pass");
                            //Console.WriteLine();
                            //refreshRootTree(0, true);
                        }
                    }

                    myChatString = chatstring(this);
                    begreedy();


                    switch (themove)
                    {
                        case -2:
                            Console.WriteLine("=pass");
                            Console.WriteLine();
                            //refreshRootTree(0, true);
                            break;
                        case -1:
                            Console.WriteLine("=resign");
                            Console.WriteLine();
                            clear();
                            break;
                        default:
                            myBoard.place(themove, thecolor);
                            Console.WriteLine("=" + Sgf.get_Sgf_Position(themove, myBoard.myboardsize));
                            Console.WriteLine();
                            myBoard.changemycolortoMove();
                            refreshRootTree(themove, false);
                            break;
                    }
                   
                    
                }
                arg = "";
                myInput = "";

            }
        }

        public static string chatstring(Game theGame)
        {
            Tree theRoot = theGame.theRoot;
            string theReturn = "=No match! ";
            Tree theChild = theGame.theRoot.getTopChild(1);
            if (theChild != null)
            {
                theReturn = "=move " + (theGame.myBoard.mymovehistory.Count+1).ToString("000") + " ";
                theReturn += Sgf.get_Sgf_Position_formated(theChild.pos, theGame.myBoard.myboardsize) + " ";
                int i = 0;
                while (theChild.getTopChild() != null && i < 3)
                {
                    theChild = theChild.getTopChild();
                    theReturn += Sgf.get_Sgf_Position_formated(theChild.pos, theGame.myBoard.myboardsize) + " ";
                    ++i;
                }

                theChild = theRoot.getTopChild(1);
                string biasout = "--.-- ";
                //if (Tree.myStartposBiasCache.ContainsKey(0) && Tree.myStartposBiasCache[0].ContainsKey(theChild.pos))
                //{
                    biasout = theChild.mybias.ToString("+0.00;-0.00") + " ";
                //}
                string theProMove = "-- ";
                if (theRoot.patternmoves1.ContainsKey(theChild.pos))
                {
                    if (theRoot.patternmoves1[theChild.pos] < 100)
                    {
                        theProMove = theRoot.patternmoves1[theChild.pos].ToString() + " ";
                    }
                    else
                    {
                        theProMove = "99 ";
                    }
                }
                theReturn += theProMove;
                theReturn += "(" + theChild.vorkommen.ToString("0000") + " of " + theRoot.vorkommen.ToString("00000")
                    + " POuts) "
                    + "Bias: " + biasout
                    + "MC: " + (theChild.q * 100).ToString("00.0") + " "
                    + "RAVE: " + (theChild.qr * 100).ToString("00.0") + " "
                    + "beta: " + Tree.getbeta(theChild).ToString(".00") + " "
                    + "Dynk: " + (theGame.getDynamicKomi()).ToString("+00.0;-00.0") + " "
                    + "Grdk: " + theGame.mygreedyKomi.ToString("+00;-00") + " "
                    + "K: " + theGame.myKomi.ToString("+00.0;-00.0") + " "
                    + "by " + (theChild.myresultpoints / theChild.vorkommen).ToString("+00.0;-00.0") + " p "
                    //+ "sk " + theGame.myBoard.sekiLibs.Count.ToString("00") + " "
                    ;
            }
            return theReturn;
        }

        private void begreedy()
        {
            //return;
            if (theRoot.getTopChild() == null) return;
            //greedy komi
            Tree thetopChild = theRoot.getTopChild();
            // be 3 points more greedy / one follows after
            if (thetopChild.q * 100 > 75 && thetopChild.qr * 100 > 75 && thetopChild.myresultpoints/thetopChild.vorkommen > 10)
            {
                if (myBoard.mytoMove == color.white)
                {
                    mygreedyKomi -= (Convert.ToInt32(thetopChild.myresultpoints / thetopChild.vorkommen) - 9)/2;
                }
                else
                {
                    mygreedyKomi += (Convert.ToInt32(thetopChild.myresultpoints / thetopChild.vorkommen) - 9) / 2;
                }
            }
            // be a point more greedy
            if (thetopChild.q * 100 > 65 && thetopChild.qr * 100 > 65 && thetopChild.myresultpoints / thetopChild.vorkommen > 6)
            {
                if (myBoard.mytoMove == color.white)
                {
                    mygreedyKomi--;
                }
                else
                {
                    mygreedyKomi++;
                }
            }
            // Reduce greedyness
            if (thetopChild.q * 100 < 60 || thetopChild.qr * 100 < 60 || thetopChild.myresultpoints / thetopChild.vorkommen < 5)
            {
                if (myBoard.mytoMove == color.white)
                {
                    if (mygreedyKomi < 0) mygreedyKomi++;
                }
                else
                {
                    if (mygreedyKomi > 0) mygreedyKomi--;
                }
            }
            // Fallback to normal play
            if (thetopChild.q * 100 < 55 || thetopChild.qr * 100 < 55 || thetopChild.myresultpoints / thetopChild.vorkommen < 2)
            {
                if ((myBoard.mytoMove == color.black && mygreedyKomi > 0)
                    || (myBoard.mytoMove == color.white && mygreedyKomi < 0))
                {
                    mygreedyKomi = 0;
                }
            }
            // only in the beginning
            if (myBoard.mymovehistory.Count < 100)
            {
                // let's wait for a chance later
                if (thetopChild.q * 100 < 42 && thetopChild.qr * 100 < 42 && thetopChild.myresultpoints / thetopChild.vorkommen < -3)
                {
                    if (myBoard.mytoMove == color.white && mygreedyKomi < 10)
                    {
                        mygreedyKomi++;
                    }
                    if (myBoard.mytoMove == color.black && mygreedyKomi > -10)
                    {
                        mygreedyKomi--;
                    }
                }
            }
            if (thetopChild.q * 100 > 42 && thetopChild.qr * 100 > 42 && thetopChild.myresultpoints / thetopChild.vorkommen > -10)
            {
                if (myBoard.mytoMove == color.white && mygreedyKomi > 0)
                {
                    mygreedyKomi--;
                }
                if (myBoard.mytoMove == color.black && mygreedyKomi < 0)
                {
                    mygreedyKomi++;
                }
            }
            if (thetopChild.q * 100 > 45 && thetopChild.qr * 100 > 45 && thetopChild.myresultpoints / thetopChild.vorkommen > -5)
            {
                if (myBoard.mytoMove == color.white && mygreedyKomi > 0)
                {
                    mygreedyKomi=0;
                }
                if (myBoard.mytoMove == color.black && mygreedyKomi < 0)
                {
                    mygreedyKomi = 0;
                }
            }
            if (myBoard.mymovehistory.Count > 200)
            {
                if (myBoard.mytoMove == color.white && mygreedyKomi > 0)
                {
                    mygreedyKomi--;
                }
                if (myBoard.mytoMove == color.black && mygreedyKomi < 0)
                {
                    mygreedyKomi++;
                }
            }
        }

        private void clear()
        {
            myBoard.myGroup.Clear();
            myBoard.myGroupPosition.Clear();
            myBoard.mymovehistory.Clear();
            myBoard.sekiLibs.Clear();
            myBoard.myWinner = color.empty;
            myBoard.mytoMove = color.black;
            myhandicap = 0;
            dopass = false;
            dogen = false;
            doquit = false;
            doplay = false;
            doclear = false;
            theRoot = new Tree(null, myBoard);
            mygreedyKomi = 0;
            Tree.mylastMoveCache1 = null;
            Tree.mylastMoveCache2 = null;
            Tree.mylastMoveCache3 = null;
        }
        public double getDynamicKomi()
        {
            if (myDynamicKomi != Double.MinValue)
            {
                return myDynamicKomi;
            }
            
            myDynamicKomi = myKomi + myhandicap + mygreedyKomi;
            if (Tree.myMoveHistory.Count < myBoard.myboardsize * myBoard.myboardsize / 3)
            {
                double factor = Math.Sqrt(Tree.myMoveHistory.Count + myhandicap) / Math.Sqrt(myBoard.myboardsize * myBoard.myboardsize / 3);
                myDynamicKomi = factor * (myKomi / 2) + myKomi / 2
                    + myhandicap * 8.5 * (1.0 - factor) + myhandicap + mygreedyKomi;
            } 
            return myDynamicKomi;
        }
    }
}
